package drds.server.net.packet;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import drds.server.buffer.ByteBufferList;
import drds.server.net.SessionContext$LifeCycle;

/**
 * From server to client. One packet for each row in the result set.
 * 
 * <pre>
 * Bytes                   Name
 * -----                   ----
 * n (Length Coded String) (column value)
 * ...
 * 
 * (column value):         The data in the column, as a character string.
 *                         If a column is defined as non-character, the
 *                         server converts the value into a character
 *                         before sending it. Since the value is a Length
 *                         Coded String, a NULL can be represented with a
 *                         single byte containing 251(see the description
 *                         of Length Coded Strings in section "Elements" above).
 * 
 * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Row_Data_Packet
 * </pre>
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class RowDataPacket extends Packet {
	private static final byte NULL_MARK = (byte) 251;
	private static final byte EMPTY_MARK = (byte) 0;

	public byte[] value;
	public int fieldCount;
	public final List<byte[]> fieldBytesList;

	public RowDataPacket(int fieldCount) {
		this.fieldCount = fieldCount;
		this.fieldBytesList = new ArrayList<byte[]>(fieldCount);
	}

	public void add(byte[] bytes) {
		// 这里应该修改value
		fieldBytesList.add(bytes);
	}

	public void addFieldCount(int add) {
		// 这里应该修改field
		fieldCount = fieldCount + add;
	}

	public void read(byte[] bytes) {
		value = bytes;
		BytesHolder bytesHolder = new BytesHolder(bytes);
		packetLength = bytesHolder.readInt3();
		packetId = bytesHolder.read();
		for (int i = 0; i < fieldCount; i++) {
			fieldBytesList.add(bytesHolder.readBytesWithLength());
		}
	}

	@Override
	public ByteBuffer write(ByteBuffer byteBuffer, SessionContext$LifeCycle sessionContext, boolean writeSocketIfFull) {
		byteBuffer = sessionContext.checkWriteByteBuffer(byteBuffer, sessionContext.getPacketHeaderSize(), writeSocketIfFull);
		ByteBufferUtil.writeInt3(byteBuffer, computePacketSize());
		byteBuffer.put(packetId);
		for (int i = 0; i < fieldCount; i++) {
			byte[] fieldBytes = fieldBytesList.get(i);
			if (fieldBytes == null) {
				byteBuffer = sessionContext.checkWriteByteBuffer(byteBuffer, 1, writeSocketIfFull);
				byteBuffer.put(RowDataPacket.NULL_MARK);
			} else if (fieldBytes.length == 0) {
				byteBuffer = sessionContext.checkWriteByteBuffer(byteBuffer, 1, writeSocketIfFull);
				byteBuffer.put(RowDataPacket.EMPTY_MARK);
			} else {
				byteBuffer = sessionContext.checkWriteByteBuffer(byteBuffer, ByteBufferUtil.getLength(fieldBytes), writeSocketIfFull);
				ByteBufferUtil.writeLength(byteBuffer, fieldBytes.length);
				byteBuffer = sessionContext.writeToByteBuffer(fieldBytes, byteBuffer);
			}
		}
		return byteBuffer;
	}

	@Override
	public int computePacketSize() {
		int size = 0;
		for (int i = 0; i < fieldCount; i++) {
			byte[] v = fieldBytesList.get(i);
			size += (v == null || v.length == 0) ? 1 : ByteBufferUtil.getLength(v);
		}
		return size;
	}

	public void write(ByteBufferList byteBufferList) {
		int size = computePacketSize();
		ByteBuffer byteBuffer = byteBufferList.checkWritingByteBufferCapacity(packetHeaderSize + size);
		ByteBufferUtil.writeInt3(byteBuffer, size);
		byteBuffer.put(packetId);
		for (int i = 0; i < fieldCount; i++) {
			byte[] fieldBytes = fieldBytesList.get(i);
			if (fieldBytes == null) {
				byteBuffer = byteBufferList.checkWritingByteBufferCapacity(1);
				byteBuffer.put(RowDataPacket.NULL_MARK);
			} else if (fieldBytes.length == 0) {
				byteBuffer = byteBufferList.checkWritingByteBufferCapacity(1);
				byteBuffer.put(RowDataPacket.EMPTY_MARK);
			} else {
				byteBuffer = byteBufferList.checkWritingByteBufferCapacity(ByteBufferUtil.getLength(fieldBytes.length));
				ByteBufferUtil.writeLength(byteBuffer, fieldBytes.length);
				byteBufferList.write(fieldBytes);
			}
		}
	}

}